#include "MyI2S.h"
#include "wave.h"
#include <SD.h>
#include <SPI.h>
#include "Base64.h"  // 引入Base64库
#include <HTTPClient.h>
#include <ArduinoJson.h>

SPIClass sdSPI(HSPI);
#define SD_MISO 17
#define SD_MOSI 16
#define SD_SCLK 18
#define SD_CS 14

MyI2S mi;
const int record_time = 4;  // 秒

const int waveDataSize = record_time * 32000;
// const int waveDataSize = 1000000;
int32_t communicationData[1024];  // 接收缓冲区
char partWavData[1024];
char* totalWavData;  // 动态分配内存

String ssid = "bobolili", password = "00000000";

int totalDataIndex = 0;  // 用于跟踪totalWavData的当前位置

// 定义连接按钮的GPIO引脚
const int buttonPin = 14; 

WAV_HEADER header;

void setup() {
  WiFi.begin(ssid, password);

  Serial.begin(115200);

  // sdSPI.begin(SD_SCLK, SD_MISO, SD_MOSI, SD_CS);
  // if (!SD.begin(SD_CS, sdSPI)) {
  //   Serial.println("存储卡挂载失败");
  //   return;
  // }

  // 动态分配内存
  totalWavData = (char*)heap_caps_malloc(waveDataSize + 44, MALLOC_CAP_SPIRAM);  //44是wav头文件,1000是冗余
  if (totalWavData == NULL) {
    Serial.println("SPI内存分配失败");
    return;
  }
  // Serial.printf("totalWavData大小");
  // Serial.println(strlen(totalWavData));
  header = CreateWaveHeader(1, 16000, 16);
  header.riffSize = waveDataSize + 44 - 8;
  header.dataSize = waveDataSize;

  if (!mi.InitInput(I2S_BITS_PER_SAMPLE_32BIT, 41, 42, 2)) {
    Serial.println("init i2s error");
    return;
  }
  // 将按钮引脚设置为输入
  pinMode(buttonPin, INPUT_PULLUP);
}

void loop() {
  if(digitalRead(buttonPin)==LOW){
      totalDataIndex = 0; 
  // 之前的初始化代码...

  memcpy(totalWavData, &header, sizeof(header));
  totalDataIndex += sizeof(header);

  Serial.println("start");

  // 录音循环
  for (int j = 0; j < waveDataSize / 1024; ++j) {
    auto sz = mi.Read((char*)communicationData, 4096);
    char* p = (char*)(communicationData);
    for (int i = 0; i < sz / 4; i++) {
      if (i % 2 == 0)  //这里获取到的数据第一个Int32是右声道
      {
        // Serial.println(communicationData[i]);
        communicationData[i] *= 64;  //提高声音
        partWavData[i] = p[4 * i + 2];
        partWavData[i + 1] = p[4 * i + 3];
      }
    }
    memcpy(totalWavData + totalDataIndex, &partWavData, sizeof(partWavData));
    totalDataIndex += sizeof(partWavData);
  }
  // 处理最后一小段音频数据
  int lastCommunicationDataLength = waveDataSize % 1024;
  auto sz = mi.Read((char*)communicationData, lastCommunicationDataLength * 4);
  char* p = (char*)(communicationData);
  for (int i = 0; i < sz / 4; i++) {
    if (i % 2 == 0)  //这里获取到的数据第一个Int32是右声道
    {
      communicationData[i] *= 64;  //提高声音
      partWavData[i] = p[4 * i + 2];
      partWavData[i + 1] = p[4 * i + 3];
    }
  }
  memcpy(totalWavData + totalDataIndex, &partWavData, lastCommunicationDataLength);
  totalDataIndex += lastCommunicationDataLength;

  Serial.println("finish");

  // 写入文件部分
  // File file = SD.open("/test4.wav", FILE_WRITE);
  // if (file) {
  //   file.write((const uint8_t*)totalWavData, totalDataIndex + 44);
  //   file.close();
  //   Serial.println("文件写入成功");
  // } else {
  //   Serial.println("文件打开失败");
  // }

  String result = processAudioData(totalWavData, totalDataIndex + 44);
  Serial.println("you:"+result);

  result = performRequest(result);
  Serial.println(result);

  // 之后的操作...
  }

}


String getAccessToken(String client_id, String client_secret) {
  HTTPClient http;
  http.begin("https://aip.baidubce.com/oauth/2.0/token");
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");
  http.addHeader("Accept", "application/json");

  String postData = "grant_type=client_credentials&client_id=" + client_id + "&client_secret=" + client_secret;
  int httpResponseCode = http.POST(postData);

  if (httpResponseCode > 0) {
    String response = http.getString();
    DynamicJsonDocument doc(1024);
    deserializeJson(doc, response);
    String accessToken = doc["access_token"].as<String>();
    http.end();
    return accessToken;
  } else {
    // Handle error or return an empty string
    http.end();
    return String("");
  }
}

String processAudioData(char* audioData, size_t dataSize) {
  String base64AudioData = base64::encode((uint8_t*)audioData, dataSize);
  // Serial.println(base64AudioData.substring(0, 1000));

  String client_id = "4GRsCVtzYuTc20Gw61Srqeoa", client_secret = "xZ5EXPXq5stseku8EhG3xtSui4S9zH2f";
  String accessToken = getAccessToken(client_id, client_secret);
  if (accessToken.length() == 0) {
    return "Error obtaining access token";
  }
  // else {
  //     Serial.println(accessToken);
  // }

  HTTPClient http;
  http.begin("https://vop.baidu.com/server_api");
  http.addHeader("Content-Type", "application/json");
  http.addHeader("Accept", "application/json");

  // 手动构建 JSON 数据
  String requestBody = "{";
  requestBody += "\"format\":\"pcm\",";
  requestBody += "\"rate\":16000,";
  requestBody += "\"channel\":1,";
  requestBody += "\"cuid\":\"B1g128gfFHxPKmlcK01V4u4Dtk013tng\",";
  requestBody += "\"token\":\"" + accessToken + "\",";
  requestBody += "\"speech\":\"" + base64AudioData + "\",";
  requestBody += "\"len\":" + String(dataSize);
  requestBody += "}";

  // Serial.println(requestBody);  // 打印完整的 JSON 请求体

  int httpResponseCode = http.POST(requestBody);
  if (httpResponseCode > 0) {
    String response = http.getString();
    http.end();
    return getResultFromJson(response);
  } else {
    http.end();
    return "Error in HTTP request";
  }
}

String getResultFromJson(String jsonString) {
  DynamicJsonDocument doc(jsonString.length() + 100);  // 分配足够的空间来存储解析后的JSON
  DeserializationError error = deserializeJson(doc, jsonString);

  if (error) {
    return "JSON解析错误";
  }

  if (!doc.containsKey("result")) {
    return "JSON中没有找到result键";
  }

  JsonArray arr = doc["result"].as<JsonArray>();
  if (arr.size() == 0) {
    return "result数组为空";
  }

  return arr[0].as<String>();  // 返回result数组的第一个元素
}
String getAccessTokenForWXYY(String client_id, String client_secret) {
  HTTPClient http;
  http.begin("https://aip.baidubce.com/oauth/2.0/token");
  http.addHeader("Content-Type", "application/x-www-form-urlencoded");
  http.addHeader("Accept", "application/json");

  String postData = "grant_type=client_credentials&client_id=" + client_id + "&client_secret=" + client_secret;
  int httpResponseCode = http.POST(postData);

  if (httpResponseCode > 0) {
    String payload = http.getString();
    DynamicJsonDocument doc(1024);
    deserializeJson(doc, payload);
    String accessToken = doc["access_token"].as<String>();
    http.end();
    return accessToken;
  } else {
    http.end();
    return "";
  }
}
String performRequest(String msg) {
  String client_id = "7mnrke7lRtCQwqQHgYiUkeBt";
  String client_secret = "LfHrIUYNk7r4GIN87Qj1oA7O5GRxqDhw";
  msg="我们玩一个角色扮演的游戏 你是一台执行指令的计算机 有这些指令函数 setClock(int hour,int minute,String remark) delClock(int hour,int minute) 你会根据我和你说的话返回指令或者是返回聊天信息 如果返回指令 那需要以SET:开头 如果返回你想说的话 那需要以WX :开头 请严格按照规定的格式来回答 下面是我说的话:"+msg;

  String accessToken = getAccessToken(client_id, client_secret);
  if (accessToken.length() <= 0) {
    Serial.println("获取访问令牌失败");
    return "";
  }

  HTTPClient http;
  http.begin("https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/completions_pro?access_token=" + accessToken);
  http.addHeader("Content-Type", "application/json");

  // String requestData = "{\"messages\":[{\"role\":\"user\",\"content\":\"你好\"},{\"role\":\"assistant\",\"content\":\"你好，请问有什么我可以帮助你的吗？\"}],\"disable_search\":false,\"enable_citation\":false}";

  String requestData = "{\"messages\":[{\"role\":\"user\",\"content\":\""+msg+"\"}],\"disable_search\":false,\"enable_citation\":false}";
  int httpResponseCode = http.POST(requestData);

  if (httpResponseCode > 0) {
    String response = http.getString();
    http.end();
    // Serial.println(response);
    return parseResultFromJson(response);
  } else {
    http.end();
    return "";
  }
}

String parseResultFromJson(String jsonString) {
  const size_t bufferSize = 4096;  // 预留一个字节用于结尾的空字符
  DynamicJsonDocument doc(bufferSize);

  // 解析JSON字符串
  DeserializationError error = deserializeJson(doc, jsonString);

  // 检查解析是否成功
  if (error) {
    Serial.print("Failed to parse JSON: ");
    Serial.println(error.c_str());
    return "";
  }

  // 获取result字段的值
  const char* result = doc["result"];

  return String(result);
}
